package chapter13.eg6;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Server {

  public static void main(String[] args) throws IOException {
    new Server().start();
  }

  private void start() throws IOException {
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.bind(new InetSocketAddress(8080));
    serverSocketChannel.configureBlocking(false);

    Selector selector = Selector.open();
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    while (true) {
      if (selector.select(1000) == 0) {
        System.out.println("has no message...");
        continue;
      }

      Set<SelectionKey> selectionKeys = selector.selectedKeys();
      Iterator<SelectionKey> iterator = selectionKeys.iterator();
      while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        iterator.remove();

        if (key.isAcceptable()) {
          accept(key, selector);
        }

        if (key.isReadable()) {
          read(key);
        }

        if (key.isValid() && key.isWritable()) {
          write(key);
        }
      }
    }
  }

  private void accept(SelectionKey key, Selector selector) throws IOException {
    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
    SocketChannel socketChannel = serverChannel.accept();
    socketChannel.configureBlocking(false);
    socketChannel.register(selector, SelectionKey.OP_READ);
  }

  private void read(SelectionKey key) throws IOException {
    SocketChannel clientChannel = (SocketChannel) key.channel();
    ByteBuffer buffer = ByteBuffer.wrap(new byte[1024]);
    int len = clientChannel.read(buffer);
    if (len == -1) {
      clientChannel.close();
      return;
    }

    System.out.println("**********server: read message**********");
    System.out.println(new String(buffer.array(), 0, len));
    key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
  }

  private void write(SelectionKey key) throws IOException {
    String message = "message from server";
    ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
    SocketChannel clientChannel = (SocketChannel) key.channel();

    if (clientChannel.isOpen()) {
      System.out.println("**********server: write message**********");
      System.out.println(message);
      clientChannel.write(buffer);
    }

    if (!buffer.hasRemaining()) {
      key.interestOps(SelectionKey.OP_READ);
    }

    buffer.compact();
  }
}
